package com.xhdd.serv;


import co.elastic.clients.elasticsearch.indices.IndexState;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MappingJsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.xhdd.dto.CodeData;
import com.xhdd.dto.MetaData;
import com.xhdd.util.CodeUtil;
import com.xhdd.util.EsUtil;
import com.xhdd.util.global.AdjoinCode;
import com.xhdd.util.global.Cell;
import com.xhdd.util.global.Params;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.springframework.stereotype.Service;
import org.wowtools.giscat.vector.pojo.converter.GeoJsonFeatureConverter;

import javax.annotation.Resource;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;

import static com.xhdd.constant.Const.*;


@Service
public class CodeServiceIml implements CodeService {

    @Resource
    private EsUtil esUtil;
    //    @Resource
//    private KafkaTemplate<String, Object> kTemplate;
    @Resource
    ObjectMapper jMapper;
    @Resource
    CodeUtil codeUtil;

    @Resource
    JsonFactory jsonFactory;



    //插入元数据并初始化数据表
    public Boolean initMetaTbDoc(MetaData data) throws IOException {
        String indexName = data.getIndexName();
        JsonNode documentJson = data.getDocumentJson();
        boolean b = esUtil.indexExists(indexName);
        if (!b) {
            return esUtil.createIndex(indexName, documentJson);
        }
        return b;
    }

    public Boolean indexDelete(String indexName) throws IOException {
        return esUtil.indexDelete(indexName);
    }

    public Map<String, IndexState> listIndex() throws IOException {
        return esUtil.listIndex();
    }

    public String code(CodeData data) throws IOException {
        String indexName = data.getIndexName();
        boolean exists = esUtil.indexExists(indexName);
        if (!exists) {
            return INXDEX_NO_EXIT;
        }
        JsonParser jp = jsonFactory.createParser(data.getInputStream());
        JsonToken current;
        current = jp.nextToken();
        if (current != JsonToken.START_OBJECT) {
            return FILE_FORMAT_ERR;
        }
        while (jp.nextToken() != JsonToken.END_OBJECT) {
            String fieldName = jp.getCurrentName();
            current = jp.nextToken();
            if (FEATURES.equals(fieldName)) {
                if (current == JsonToken.START_ARRAY) {
                    while (jp.nextToken() != JsonToken.END_ARRAY) {
                        JsonNode node = jp.readValueAsTree();
                        bdCode(indexName, null, node, Integer.parseInt(data.getLevel()));
                    }
                } else {
                    jp.skipChildren();
                }
            } else {
                jp.skipChildren();
            }
        }
        return indexName + CODING;
    }

    public void bdCode(String indexName, String id, JsonNode doc, int level) throws IOException {
        JsonNode properties = doc.get(PROPERTIES);
        JsonNode geo = doc.get(GEOMETRY);
        Geometry geometry = GeoJsonFeatureConverter.geoJson2Geometry(geo.toPrettyString(), new GeometryFactory());
        ObjectNode document = jMapper.createObjectNode();
        properties.fields().forEachRemaining(entry -> document.put(entry.getKey(), entry.getValue()));
        document.put(GEOMETRY, geo.toPrettyString());
        String geometryType = geometry.getGeometryType().toLowerCase();
        try {
            List<String> bdCodes = getBDCode(geometryType, geometry, level);
            document.put(BDCODE, Arrays.toString(bdCodes.toArray(String[]::new)));
        } catch (Exception e) {
//            sendError(indexName, doc.toPrettyString());
            e.printStackTrace();
        }
        insertDoc(indexName, id, document);
    }

    private List<String> getBDCode(String geometryType, Geometry geometry, int level) throws ParseException {
        List<String> codes = switch (geometryType) {
            case POLYGON -> codeUtil.faceCode((Polygon) geometry, level);
            case MULTIPOLYGON -> codeUtil.multiPolygonCode((MultiPolygon) geometry, level);
            case MULTIPOINT -> codeUtil.multiPointCode((MultiPoint) geometry, level);
            case MULTILINESTRING -> codeUtil.multiLineStringCode((MultiLineString) geometry, level);
            case LINESTRING -> codeUtil.lineCode((LineString) geometry, level);
            case POINT -> codeUtil.pointCode((Point) geometry, level);
            default -> null;
        };
        return codes;
    }

//    private void sendError(String topic, String message) {
//        kTemplate.send(topic, message);
//    }

    public String insertDoc(String indexName, String id, JsonNode doc) throws IOException {
        return esUtil.insertDoc(indexName, id, doc);
    }


    public ArrayNode searchDoc(String indexName, List<Params> params, Integer page, Integer size) throws IOException {
        return esUtil.searchDoc(indexName, params, page, size);
    }

    public ArrayNode searchPrefix(String indexName, List<String> prefixs, Integer page, Integer size) throws IOException {
        Integer tableLevel = 0;
        List<String> list = new ArrayList<>();
        for (String prefix : prefixs) {
            if (prefix.length() > tableLevel) {
                prefix = prefix.substring(0, tableLevel);
            }
            list.add(prefix);
        }
        return esUtil.searchPrefix(indexName, list, page, size);
    }

    public ArrayNode search(String indexName, List<String> prefixs, List<Params> params, Integer page, Integer size) throws IOException {
        Integer tableLevel = 0;
        List<String> list = new ArrayList<>();
        for (String prefix : prefixs) {
            if (prefix.length() > tableLevel) {
                prefix = prefix.substring(0, tableLevel);
            }
            list.add(prefix);
        }
        return esUtil.search(indexName, list, params, page, size);
    }

    public Map<String, Double> propAggs(String indexName, String prop) throws IOException {
        return esUtil.propAggs(indexName, prop);
    }

    /*去重统计在非分词类型上结果准确*/
    public long noRepeatCount(String indexName, String prop) throws IOException {
        return esUtil.noRepeatCount(indexName, prop);
    }

    /*分组，热力图*/
    public HashMap<String, Long> groupBy(String indexName, List<Params> params, String prop, String type) throws IOException {

        return esUtil.groupBy(indexName, params, prop, type);
    }

    public List<Cell> codes2Dj(List<String> codes) {
        List<Cell> cells = new ArrayList<>();
        for (String code : codes) {
            Cell cell = codeUtil.code2Kdj(code);
            cell.divK();
        }
        return cells;
    }

    @Override
    public double getStep(Integer level) {
        return codeUtil.getStep(level);
    }

    public AdjoinCode get4adjoin(String code) {
        return codeUtil.get4adjoin(code);
    }

    /*-------------------------------------------------------------------------*/
    /* step: 网格大小，1m为最小网格，再小不剖分
     * lng:len
     * lat:wid
     * */
    public void dealFile(String in, String indexName) throws Exception {
        int i = 0;
        ObjectNode objectNode = null;
        String id = "";
        StringBuilder sb = new StringBuilder();
        TreeNode tmpNode = null;
        JsonFactory jsonFactory = new MappingJsonFactory();
        FileInputStream stream = new FileInputStream(in);
        JsonParser jp = jsonFactory.createParser(stream);
        while (!jp.isClosed()) {
            if (jp.nextToken() == JsonToken.FIELD_NAME) {
                String fieldName = jp.currentName();
                if (fieldName.equals("features")) {
                    if (jp.nextToken() == JsonToken.START_ARRAY) {
                        while (jp.nextToken() != JsonToken.END_ARRAY) {
                            TreeNode treeNode = jp.readValueAsTree();
                            TreeNode properties = treeNode.get("properties");
                            String id1 = properties.get("ID").toString().replace("\"", "");
                            String xy = properties.get("行列").toString().replace("\"", "");
                            if (id.equals(id1)) {
                                sb.append(",").append(xy);
                            } else {
                                try {
                                    if (!sb.isEmpty()) {
                                        String[] split = sb.toString().split(",");
                                        StringBuilder codes = new StringBuilder();
                                        for (String s : split) {
                                            String[] arr = s.split("-");
                                            double x = Integer.valueOf(arr[0]) * 2 - 0.5;
                                            double y = Integer.valueOf(arr[1]) * 2 - 0.5;
                                            StringBuilder code = new StringBuilder();
                                            codeUtil.partCode(x, y, -512, 512, -512, 512, code, 2, x, y);
                                            codes.append(",").append(code);
                                        }
                                        objectNode = (ObjectNode) tmpNode;
                                        ObjectNode propNode = (ObjectNode) objectNode.get("properties");
                                        String substring = codes.substring(1);
                                        propNode.put("bdcodes", substring);
                                        propNode.remove("行列");
                                        propNode.put("xy", sb.toString());
                                        String s = objectNode.get("geometry").toString();
                                        propNode.put("geometry", s);
                                        esUtil.insertDoc(indexName, null, propNode);
                                        System.out.println(i++);
                                    }
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }

                                id = id1.toString();
                                sb.setLength(0);
                                sb.append(xy);
                                tmpNode = treeNode;
                            }
                        }
                        try {
                            if (!sb.isEmpty()) {
                                String[] split = sb.toString().split(",");
                                StringBuilder codes = new StringBuilder();
                                for (String s : split) {
                                    String[] arr = s.split("-");
                                    double x = Integer.valueOf(arr[0]) * 2 - 0.5;
                                    double y = Integer.valueOf(arr[1]) * 2 - 0.5;
                                    StringBuilder code = new StringBuilder();
                                    codeUtil.partCode(x, y, -512, 512, -512, 512, code, 2, x, y);
                                    codes.append(",").append(code);
                                }
                                objectNode = (ObjectNode) tmpNode;
                                ObjectNode propNode = (ObjectNode) objectNode.get("properties");
                                String substring = codes.substring(1);
                                propNode.put("bdcodes", substring);
                                propNode.remove("行列");
                                propNode.put("xy", sb.toString());
                                String s = objectNode.get("geometry").toString();
                                propNode.put("geometry", s);
                                esUtil.insertDoc(indexName, null, propNode);
                                System.out.println(i++);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                } else {
                    jp.skipChildren();
                }
            }
        }
    }
}

